package com.brtbeacon.demo.lot;

import android.bluetooth.BluetoothAdapter;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;

import com.brtbeacon.demo.lot.adapter.FloorAdapter;
import com.brtbeacon.demo.lot.adapter.FloorViewHolder;
import com.brtbeacon.demo.lot.entity.FloorInfo;
import com.brtbeacon.demo.lot.entity.ParkingParams;
import com.brtbeacon.demo.lot.speech.SpeechEngine;
import com.brtbeacon.demo.lot.state.ParkingNavigateState;
import com.brtbeacon.demo.lot.state.ParkingRouteState;
import com.brtbeacon.demo.lot.state.ParkingState;
import com.brtbeacon.demo.lot.state.ParkingTargetState;
import com.brtbeacon.demo.lot.state.ParkingWaitLocationState;
import com.brtbeacon.demo.lot.utils.DefaultLocationManagerListener;
import com.brtbeacon.locationengine.ble.BRTLocationManager;
import com.brtbeacon.locationengine.ble.BRTPublicBeacon;
import com.brtbeacon.locationengine.ibeacon.BeaconService;
import com.brtbeacon.map.map3d.BRTMapEnvironment;
import com.brtbeacon.map.map3d.BRTMapView;
import com.brtbeacon.map.map3d.entity.BRTFloorInfo;
import com.brtbeacon.map.map3d.entity.BRTPoi;
import com.brtbeacon.map.map3d.entity.BRTPoiEntity;
import com.brtbeacon.map.map3d.entity.BRTPoint;
import com.brtbeacon.map.map3d.route.BRTMapRouteManager;
import com.brtbeacon.map.map3d.route.BRTRouteResult;
import com.brtbeacon.map.map3d.utils.BRTConvert;
import com.brtbeacon.map.map3d.utils.BRTSearchAdapter;
import com.brtbeacon.mapdata.BRTLocalPoint;
import com.google.gson.JsonObject;
import com.mapbox.geojson.Feature;
import com.mapbox.geojson.FeatureCollection;
import com.mapbox.geojson.Point;
import com.mapbox.mapboxsdk.camera.CameraPosition;
import com.mapbox.mapboxsdk.geometry.LatLng;
import com.mapbox.mapboxsdk.maps.MapboxMap;
import com.mapbox.mapboxsdk.style.expressions.Expression;
import com.mapbox.mapboxsdk.style.layers.CircleLayer;
import com.mapbox.mapboxsdk.style.layers.PropertyFactory;
import com.mapbox.mapboxsdk.style.layers.PropertyValue;
import com.mapbox.mapboxsdk.style.layers.SymbolLayer;
import com.mapbox.mapboxsdk.style.sources.GeoJsonSource;
import com.mapbox.mapboxsdk.utils.ColorUtils;
import com.vividsolutions.jts.geom.Coordinate;

import com.mapbox.mapboxsdk.style.layers.PropertyFactory.*;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import static com.mapbox.mapboxsdk.style.expressions.Expression.eq;
import static com.mapbox.mapboxsdk.style.expressions.Expression.get;
import static com.mapbox.mapboxsdk.style.expressions.Expression.literal;

public class LotMapActivity extends AppCompatActivity implements View.OnClickListener ,BRTMapView.BRTMapViewListener{

    public static final String TAG = LotMapActivity.class.getSimpleName();
    private static final int BRTMAP_PERMISSION_CODE = 999;
    public static final String ARG_PARKING_PARAMS = "arg_parking_params";

    protected ParkingParams parkingParams;

    private RecyclerView floorListView;
    private FloorAdapter floorAdapter;
    private Map<Integer, ParkingState> stateMap = new HashMap<>();
    private int activeStateId = -1;
    private ParkingState activeState = null;

    private BRTMapView mapView;

    private BRTLocationManager locationManager;
    private long lastLocationTimeMillis = 0;
    private long locationExpirationMillis = 15000;
    private BRTLocalPoint lastLocation;

    private BRTPoint startPoint;
    private BRTPoint endPoint;
    private BRTMapRouteManager routeManager;

    private BRTSearchAdapter searchAdapter = null;
    private String parkingPoiName = null;
    private BRTPoi parkingPoi = null;

    private TextView tvMoveMode;
    private int navMode = 1;   //  导航模式：车行1 人行0
    private float forwardDistanceDriving = 8.0f;
    private float forwardDistanceWalking = 0.0f;
    private float forwardDistanceSpeakDriving = 8.0f;
    private float forwardDistanceSpeakWalking = 3.0f;
    private float distanceToEndDriving = 4.0f;
    private float distanceToEndWalking = 4.0f;
    private float distanceToReplanRoute = 13.5f;
    private float zoomLevelDefault = 0.0f;
    private float zoomLevelNavigation = 0.0f;

    private Handler handler = new Handler();

    private SpeechEngine speechEngine = null;

    private boolean isSimLocation = false;
    private double lastDeviceHeading = 0;
    private boolean showActualLocation = false;
    private boolean showLocationBeacons = false;

    private final static String LOCATION_ARROW_HUMAN = "location_arrow_human";
    private final static String LOCATION_ARROW_CAR = "location_arrow_car";

    private final static String ACTUAL_LOCATION_SOURCE = "actual_location_source";
    private final static String ACTUAL_LOCATION_LAYER = "actual_location_layer";
    private GeoJsonSource actualLocationSource = null;
    private CircleLayer actualLocationLayer = null;

    private final static String LOCATION_BEACON_SOURCE = "location_beacon_source";
    private final static String LOCATION_BEACON_POINT_LAYER = "location_beacon_point_layer";
    private final static String LOCATION_BEACON_TEXT_LAYER = "location_beacon_text_layer";
    private GeoJsonSource locationBeaconSource = null;
    private CircleLayer locationBeaconPointLayer = null;
    private SymbolLayer locationBeaconTextLayer = null;

    public LotMapActivity() {

    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        parkingParams = getIntent().getParcelableExtra(ARG_PARKING_PARAMS);
        if (parkingParams == null)
            parkingParams = new ParkingParams();
        parkingPoiName = parkingParams.getTargetPoiName();
        if(parkingPoiName != null) {
            parkingPoiName = parkingPoiName.trim();
        }
        isSimLocation = parkingParams.isCustomLocation();
        navMode = parkingParams.getNavMode();
        setForwardDistanceDriving(parkingParams.getForwardDistanceDriving());
        setForwardDistanceWalking(parkingParams.getForwardDistanceWalking());
        setForwardDistanceSpeakDriving(parkingParams.getForwardDistanceSpeakDriving());
        setForwardDistanceSpeakWalking(parkingParams.getForwardDistanceSpeakWalking());
        setDistanceToEndDriving(parkingParams.getDistanceToEndDriving());
        setDistanceToEndWalking(parkingParams.getDistanceToEndWalking());
        setDistanceToReplanRoute(parkingParams.getDistanceToReplanRoute());
        setZoomLevelDefault(parkingParams.getZoomLevelDefault());
        setZoomLevelNavigation(parkingParams.getZoomLevelNavigation());
        setShowActualLocation(parkingParams.isShowActualLocation());
        setLocationBeaconVisible(parkingParams.isShowLocationBeacons());

        BRTMapEnvironment.initMapEnvironment(this);
        BeaconService.setTimeWindowMillis(4000);
        BeaconService.setExpirationMillis(4000);

        setContentView(R.layout.bb_activity_lot_map);

        //Toolbar toolbar = findViewById(R.id.toolbar);
        //setSupportActionBar(toolbar);
        //getSupportActionBar().setTitle(null);
        //getSupportActionBar().setDisplayHomeAsUpEnabled(true);

        floorListView = findViewById(R.id.rv_floors);
        floorListView.setLayoutManager(new LinearLayoutManager(this));

        floorAdapter = new FloorAdapter(this, new ArrayList<>());
        floorAdapter.setOnClickListener(floorOnClickListener);
        floorListView.setAdapter(floorAdapter);

        findViewById(R.id.iv_location).setOnClickListener(this);
        findViewById(R.id.iv_exit).setOnClickListener(this);
        tvMoveMode = findViewById(R.id.tv_move_mode);
        tvMoveMode.setOnClickListener(this);

        updateMoveModeView();

        mapView = findViewById(R.id.mapView);
        mapView.onCreate(savedInstanceState);
        mapView.addMapListener(this);

        mapView.setLogoVisible(View.GONE);

        if (!checkNeedPermission()) {
            initMapView();
        }
    }

    public void setTopFragment(Fragment fragment) {
        setFragment(R.id.content_top, fragment);
    }

    public void setBottomFragment(Fragment fragment) {
        setFragment(R.id.content_bottom, fragment);
    }

    public void setFragment(int id, Fragment fragment) {
        Fragment currentFragment = getSupportFragmentManager().findFragmentById(id);

        if (currentFragment == fragment)
            return;

        if (fragment != null) {
            getSupportFragmentManager().beginTransaction().replace(id, fragment).commit();
        } else if (currentFragment != null) {
            getSupportFragmentManager().beginTransaction().remove(currentFragment).commit();
        }
    }

    public void setState(int state) {
        if (activeStateId == state)
            return;

        if (activeState != null) {
            activeState.exit();
        }

        activeState = getStateInstance(state);
        if (activeState != null) {
            activeState.enter(this);
        }
    }

    private ParkingState getStateInstance(int stateId) {
        ParkingState state = stateMap.get(stateId);
        if (state == null) {
            switch (stateId) {
                case ParkingState.PARKING_STATE_TARGET: {
                    state = new ParkingTargetState();
                    break;
                }
                case ParkingState.PARKING_STATE_ROUTE: {
                    state = new ParkingRouteState();
                    break;
                }
                case ParkingState.PARKING_STATE_NAVIGATION: {
                    state = new ParkingNavigateState();
                    break;
                }
                case ParkingState.PARKING_STATE_WAIT_LOCATION: {
                    state = new ParkingWaitLocationState();
                    break;
                }
            }
        }
        return state;
    }

    private FloorAdapter.OnClickListener floorOnClickListener = new FloorAdapter.OnClickListener() {
        @Override
        public void onClick(FloorViewHolder holder) {
            floorAdapter.selectFloor(holder.getAdapterPosition());
            FloorInfo info = floorAdapter.getSelectFloor();
            if (info != null) {
                mapView.setFloor(info.getInfo());
            }
        }
    };

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.iv_location: {
                if (activeState != null) {
                    activeState.onFocusLocation();
                }
                break;
            }

            case R.id.iv_exit: {
                finish();
                break;
            }

            case R.id.tv_move_mode: {
                navMode = (navMode == 0)?1:0;
                updateMoveModeView();
                break;
            }
        }
    }

    private void updateMoveModeView() {
        tvMoveMode.setText( navMode==1 ?"车行":"人行");
        if (mapView != null && mapView.isInitSuccess() && !isFinishing()) {
            updateLocationImage();
        }
    }

    private void updateLocationImage() {
        if (navMode == 1) {
            mapView.setLocationImage(LOCATION_ARROW_CAR);
        } else {
            mapView.setLocationImage(LOCATION_ARROW_HUMAN);
        }
    }

    private void initMapView() {
        mapView.init(parkingParams.getBuildingId(), parkingParams.getAppkey(), BRTMapView.MAP_LOAD_MODE_OFFLINE);
        speechEngine = SpeechEngine.create(getApplicationContext());
    }

    @Override
    public void onStart() {
        super.onStart();
        mapView.onStart();
    }

    @Override
    public void onResume() {
        super.onResume();
        mapView.onResume();
        if (BluetoothAdapter.getDefaultAdapter() != null && !BluetoothAdapter.getDefaultAdapter().isEnabled()) {
            BluetoothAdapter.getDefaultAdapter().enable();
        }
    }

    @Override
    public void onPause() {
        super.onPause();
        mapView.onPause();
    }

    @Override
    public void onStop() {
        super.onStop();
        mapView.onStop();
    }

    @Override
    public void onLowMemory() {
        super.onLowMemory();
        mapView.onLowMemory();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        stopLocation();
        handler.removeCallbacksAndMessages(null);
        mapView.onDestroy();
        if (speechEngine != null) {
            speechEngine.close();
        }
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        mapView.onSaveInstanceState(outState);
    }

    public BRTMapView getMapView() {
        return mapView;
    }

    // BRTMapView.BRTMapViewListener 接口实现-开始

    @Override
    public void mapViewDidLoad(BRTMapView brtMapView, Error error) {
        if (error != null) {
            showToast(error.getMessage());
            return;
        }

        initActualLocationLayer(brtMapView);
        initLocationBeaconLayer(brtMapView);

        setLocationBeaconVisible(showLocationBeacons);

        brtMapView.getMap().addOnCameraMoveListener(new MapboxMap.OnCameraMoveListener() {
            @Override
            public void onCameraMove() {
                Log.d(TAG, "zoom: " + mapView.getMap().getCameraPosition().zoom);
            }
        });

        Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.bb_location_arrow2);
        mapView.getMap().getStyle().addImage(LOCATION_ARROW_HUMAN, bitmap);

        bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.bb_location_car);
        mapView.getMap().getStyle().addImage(LOCATION_ARROW_CAR, bitmap);

        //mapView.setLocationImage(bitmap);
        updateLocationImage();
        mapView.setRouteEndSymbol(BitmapFactory.decodeResource(getResources(), R.drawable.icon_parking));

        List<FloorInfo> floorList = new ArrayList<>();
        for (BRTFloorInfo brtFloorInfo: mapView.getFloorList()) {
            floorList.add(new FloorInfo(brtFloorInfo));
        }
        floorAdapter.setFloors(floorList);

        mapView.setFloor(mapView.getFloorList().get(0));
        if (zoomLevelDefault > 0) {
            mapView.setZoom(zoomLevelDefault);
        }

        searchAdapter = new BRTSearchAdapter(mapView.getBuilding().getBuildingID());
        BRTPoi poi = queryParkingPoi();
        if (poi != null) {
            setParkingEndPoi(poi);
        }

        handler.postDelayed(new Runnable() {
            @Override
            public void run() {
                setState(ParkingState.PARKING_STATE_WAIT_LOCATION);
            }
        }, 1000);

        initRouteEngine();
        initLocationEngine();
        startLocation();

    }

    private void initActualLocationLayer(BRTMapView mapView) {
        actualLocationSource = new GeoJsonSource(ACTUAL_LOCATION_SOURCE);
        mapView.getMap().getStyle().addSource(actualLocationSource);

        actualLocationLayer = new CircleLayer(ACTUAL_LOCATION_LAYER, ACTUAL_LOCATION_SOURCE);
        mapView.getMap().getStyle().addLayer(actualLocationLayer);

        actualLocationLayer.setFilter(eq(get("floor"), Integer.MIN_VALUE));
        actualLocationLayer.setProperties(
                PropertyFactory.circleRadius(2.0f),
                PropertyFactory.circleColor(0xFF0000FF),
                PropertyFactory.circleStrokeWidth(1.0f),
                PropertyFactory.circleStrokeColor(0xFFFFFFFF));
    }

    public void setLocationBeaconVisible(boolean visible) {
        showLocationBeacons = visible;
        String value = visible ? "visible" : "none";
        if (locationBeaconPointLayer != null && locationBeaconTextLayer != null) {
            locationBeaconPointLayer.setProperties(new PropertyValue<>("visibility", value));
            locationBeaconTextLayer.setProperties(new PropertyValue<>("visibility", value));
        }
    }

    private void initLocationBeaconLayer(BRTMapView mapView) {
        locationBeaconSource = new GeoJsonSource(LOCATION_BEACON_SOURCE);
        mapView.getMap().getStyle().addSource(locationBeaconSource);

        locationBeaconPointLayer = new CircleLayer(LOCATION_BEACON_POINT_LAYER, LOCATION_BEACON_SOURCE);
        List<PropertyValue<?>> propertyValues = new ArrayList<>();
        propertyValues.add(PropertyFactory.circleColor(Expression.get("color")));
        propertyValues.add(PropertyFactory.circleStrokeColor(Expression.get("outline_color")));
        propertyValues.add(PropertyFactory.circleStrokeWidth(1.0f));
        locationBeaconPointLayer.setProperties(propertyValues.toArray(new PropertyValue<?>[]{}));
        mapView.getMap().getStyle().addLayer(locationBeaconPointLayer);

        locationBeaconTextLayer = new SymbolLayer(LOCATION_BEACON_TEXT_LAYER, LOCATION_BEACON_SOURCE);
        propertyValues.clear();
        propertyValues.add(PropertyFactory.textField(Expression.get("caption")));
        propertyValues.add(PropertyFactory.textColor(Expression.get("color")));
        propertyValues.add(PropertyFactory.textHaloColor(Expression.get("outline_color")));
        propertyValues.add(PropertyFactory.textHaloWidth(1.0f));
        propertyValues.add(PropertyFactory.textSize(10.0f));
        propertyValues.add(PropertyFactory.textAllowOverlap(true));
        propertyValues.add(PropertyFactory.textIgnorePlacement(true));
        propertyValues.add(PropertyFactory.textOffset(new Float[] { 0.0f, -1.2f }));
        locationBeaconTextLayer.setProperties(propertyValues.toArray(new PropertyValue<?>[]{}));

        locationBeaconPointLayer.setFilter(eq(get("floor"), Integer.MIN_VALUE));
        locationBeaconTextLayer.setFilter(eq(get("floor"), Integer.MIN_VALUE));
    }

    private void refreshLocationBeacons(List<BRTPublicBeacon> beacons) {
        ArrayList<Feature> features = new ArrayList<>();
        for (BRTPublicBeacon beacon: beacons) {
            BRTLocalPoint localPoint = beacon.getLocation();
            LatLng latLng = BRTConvert.toLatLng(localPoint.getX(), localPoint.getY());
            Feature feature = Feature.fromGeometry(Point.fromLngLat(latLng.getLongitude(), latLng.getLatitude()));
            feature.addNumberProperty("floor", localPoint.getFloor());
            feature.addStringProperty("caption", String.valueOf(beacon.getMinor()));
            feature.addStringProperty("color", ColorUtils.colorToRgbaString(0xFF21B393));
            feature.addStringProperty("outline_color", ColorUtils.colorToRgbaString(0xFFFFFFFF));
        }
        locationBeaconSource.setGeoJson(FeatureCollection.fromFeatures(features));
    }

    @Override
    public void onFinishLoadingFloor(BRTMapView brtMapView, BRTFloorInfo brtFloorInfo) {
        if (isDestroyed())
            return;
        floorAdapter.selectFloorByNumber(brtFloorInfo.getFloorNumber());
        mapView.getMap().setMaxZoomPreference(22);
        //mapView.setZoom(mapView.getZoom() + 1.00f);
        int floor = brtFloorInfo.getFloorNumber();
        actualLocationLayer.setFilter(eq(get("floor"), floor));
        locationBeaconPointLayer.setFilter(eq(get("floor"), floor));
        locationBeaconTextLayer.setFilter(eq(get("floor"), floor));
    }

    public BRTFloorInfo getCurrentFloor() {
        return mapView.getCurrentFloor();
    }

    @Override
    public void onClickAtPoint(BRTMapView brtMapView, BRTPoint brtPoint) {

        if (activeState != null) {
            //debug
            if (isSimLocation) {
                activeState.didUpdateLocation(null, BRTConvert.toLocalPoint(brtPoint));
            }


            activeState.onClickAtPoint(brtMapView, brtPoint);
        }
    }

    @Override
    public void onPoiSelected(BRTMapView brtMapView, List<BRTPoi> list) {
        if (activeState != null) {
            activeState.onPoiSelected(brtMapView, list);
        }
    }

    public void setParkingEndPoi(BRTPoi poi) {
        parkingPoi = poi;
        if (parkingPoi != null) {
            mapView.setRouteEnd(parkingPoi.getPoint());
            this.endPoint = parkingPoi.getPoint();
        } else {
            mapView.setRouteEnd(null);
            this.endPoint = null;
        }
        mapView.highlightPoi(parkingPoi);
    }

    public void setRouteEnd(BRTPoint point) {
        mapView.setRouteEnd(point);
        this.endPoint = point;
        //requestRoute();
    }

    // BRTMapView.BRTMapViewListener 接口实现-结束

    private void initLocationEngine() {
        locationManager = new BRTLocationManager(this, parkingParams.getBuildingId(), parkingParams.getAppkey());
        locationManager.addLocationEngineListener(locationManagerListener);
        locationManager.setLimitBeaconNumber(true);
        locationManager.setMaxBeaconNumberForProcessing(5);
        locationManager.setRssiThreshold(-88);
        locationManager.setInitAngle(mapView.getBuilding().getInitAngle());
    }

    private void startLocation() {
        if (locationManager != null) {
            locationManager.startUpdateLocation();
        }
    }

    private void stopLocation() {
        if (locationManager != null) {
            locationManager.stopUpdateLocation();
        }
    }

    public void focusLocation() {
        if (isDestroyed())
            return;

        if (lastLocation == null) {
            showToast("未获取到你的位置信息！");
        } else {
            MapboxMap map = mapView.getMap();
            CameraPosition currentPosition = map.getCameraPosition();
            if(mapView.getCurrentFloor().getFloorNumber() != lastLocation.getFloor()) {
                mapView.setFloorByNumber(lastLocation.getFloor());
            }
            //MapboxMap map = mapView.getMap();
            //CameraPosition currentPosition = map.getCameraPosition();
            LatLng latLng = BRTConvert.toLatLng(lastLocation.getX(), lastLocation.getY());

            CameraPosition newPosition = new CameraPosition.Builder()
                    .target(latLng)
                    .bearing(currentPosition.bearing)
                    .tilt(currentPosition.tilt)
                    .zoom(currentPosition.zoom)
                    .build();
            map.setCameraPosition(newPosition);
            //map.easeCamera(CameraUpdateFactory.newCameraPosition(newPosition), 0);
        }
    }

    //  定位回调处理
    private DefaultLocationManagerListener locationManagerListener = new DefaultLocationManagerListener() {

        @Override
        public void didUpdateImmediateLocation(BRTLocationManager BRTLocationManager, final BRTLocalPoint tyLocalPoint) {
            handler.post(new Runnable() {
                @Override
                public void run() {
                    if (isDestroyed())
                        return;

                    if (getNavMode() == 0)
                        return;

                    if (activeState != null) {
                        activeState.didUpdateLocation(BRTLocationManager, tyLocalPoint);
                    }

                    if (parkingPoi == null) {
                        BRTPoi poi = queryParkingPoi();
                        if (poi != null) {
                            setParkingEndPoi(poi);
                        }
                    }
                    lastLocationTimeMillis = Calendar.getInstance().getTimeInMillis();
                    startLocationExpirationCheck();
                }
            });
        }

        @Override
        public void didUpdateLocation(BRTLocationManager brtLocationManager, BRTLocalPoint tyLocalPoint) {
            super.didUpdateLocation(brtLocationManager, tyLocalPoint);
            handler.post(new Runnable() {
                @Override
                public void run() {
                    if (isDestroyed())
                        return;

                    if (getNavMode() == 1)
                        return;

                    if (activeState != null) {
                        activeState.didUpdateLocation(brtLocationManager, tyLocalPoint);
                    }

                    if (parkingPoi == null) {
                        BRTPoi poi = queryParkingPoi();
                        if (poi != null) {
                            setParkingEndPoi(poi);
                        }
                    }
                    lastLocationTimeMillis = Calendar.getInstance().getTimeInMillis();
                    startLocationExpirationCheck();
                }
            });
        }

        @Override
        public void didUpdateDeviceHeading(BRTLocationManager brtLocationManager, double v) {
            super.didUpdateDeviceHeading(brtLocationManager, v);
            mapView.processDeviceRotation(v-mapView.getBuilding().getInitAngle());
            lastDeviceHeading = v-mapView.getBuilding().getInitAngle();
        }

        @Override
        public void didRangedLocationBeacons(BRTLocationManager brtLocationManager, List<BRTPublicBeacon> list) {
            super.didRangedLocationBeacons(brtLocationManager, list);
            if (activeState != null) {
                activeState.didRangedLocationBeacons(brtLocationManager, list);
            }
            refreshLocationBeacons(list);
        }
    };

    public void setLocation(BRTLocalPoint point) {
        if (point == null) {
            mapView.setLocation(null);
            mapView.showRoutePassed(null);
        } else {
            mapView.setLocation(new BRTPoint(point.getFloor(), BRTConvert.toLatLng(point.getX(), point.getY())));
            mapView.showRoutePassed(new BRTPoint(point.getFloor(), BRTConvert.toLatLng(point.getX(), point.getY())));
        }

        BRTLocalPoint location = lastLocation;
        lastLocation = point;

        if (location == null) {
            focusLocation();
        }
    }

    public void setActualLocation(BRTLocalPoint point) {
        if (actualLocationSource == null)
            return;
        List<Feature> features = new ArrayList<>();
        if (point != null) {
            JsonObject jsonObject = new JsonObject();
            BRTPoint bp = BRTPoint.from(point);
            jsonObject.addProperty("floor", bp.getFloorNumber());
            features.add(Feature.fromGeometry(Point.fromLngLat(bp.getLongitude(), bp.getLatitude()), jsonObject));
        }
        actualLocationSource.setGeoJson(FeatureCollection.fromFeatures(features));
    }

    public BRTLocalPoint getLastLocation() {
        return lastLocation;
    }

    public double getLastDeviceHeading() {
        return lastDeviceHeading;
    }

    private void startLocationExpirationCheck() {
        handler.removeCallbacks(locationExpirationCheckRunnable);
        handler.postDelayed(locationExpirationCheckRunnable, locationExpirationMillis);
    }

    private Runnable locationExpirationCheckRunnable = new Runnable() {
        @Override
        public void run() {
            if (Calendar.getInstance().getTimeInMillis() - lastLocationTimeMillis >= locationExpirationMillis) {
                if (lastLocation == null) {
                    showToast("获取位置信息超时！");
                } else {
                    showToast("无法获取到室内位置信息！");
                    lastLocation = null;
                }
                mapView.setLocation(null);
            }
        }
    };

    public BRTPoint getEndPoint() {
        return endPoint;
    }

    public BRTPoint getStartPoint() {
        return startPoint;
    }

    public BRTRouteResult getRouteResult() {
        return mapView.getRouteResult();
    }

    public void clearRouteResult() {
        mapView.setRouteResult(null);
    }


    private void initRouteEngine() {
        routeManager = new BRTMapRouteManager(mapView.getBuilding(), parkingParams.getAppkey(), mapView.getFloorList(), false);
        routeManager.addRouteManagerListener(routeManagerListener);
    }

    public void requestRoute() {

        if (isDestroyed())
            return;

        mapView.setRouteResult(null);

        if (lastLocation == null) {
            return;
        }

        if (endPoint == null) {
            return;
        }
        startPoint = new BRTPoint(lastLocation.getFloor(), BRTConvert.toLatLng(lastLocation.getX(), lastLocation.getY()));
        mapView.setRouteStart(startPoint);

        if (routeManager == null)
            return;

        if(navMode == 1) {
            routeManager.requestRoute(startPoint, endPoint, null, false, true, null);
        } else {
            routeManager.requestRoute(startPoint, endPoint);
        }
    }

    private BRTMapRouteManager.BRTRouteManagerListener routeManagerListener = new BRTMapRouteManager.BRTRouteManagerListener() {

        @Override
        public void didSolveRouteWithResult(BRTMapRouteManager routeManager, BRTRouteResult routeResult) {
            handler.post(new Runnable() {
                @Override
                public void run() {
                    if (isDestroyed())
                        return;

                    mapView.setRouteResult(routeResult);
                    if (activeState != null) {
                        activeState.didSolveRouteWithResult(routeManager, routeResult);
                    }
                }
            });
        }

        @Override
        public void didFailSolveRouteWithError(BRTMapRouteManager routeManager, BRTMapRouteManager.BRTRouteException e) {
            handler.post(new Runnable() {
                @Override
                public void run() {
                    showToast(e.getMessage());
                }
            });
        }
    };

    private Toast mToast;
    public void showToast(String message) {
        if (mToast != null) {
            mToast.setText(message);
            mToast.setDuration(Toast.LENGTH_SHORT);
        } else {
            mToast = Toast.makeText(this, message, Toast.LENGTH_SHORT);
        }
        mToast.show();
    }

    private boolean checkNeedPermission() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {//判断当前系统的SDK版本是否大于23
            List<String> permissionNeedRequest = new LinkedList<>();
            for (String permssion: permissionsNeedCheck) {
                if(ActivityCompat.checkSelfPermission(this, permssion) != PackageManager.PERMISSION_GRANTED) {
                    permissionNeedRequest.add(permssion);
                }
            }
            if (!permissionNeedRequest.isEmpty()) {
                ActivityCompat.requestPermissions(this, permissionNeedRequest.toArray(new String[0]), BRTMAP_PERMISSION_CODE);
                return true;
            }
        }

        return false;
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        switch (requestCode) {
            // requestCode即所声明的权限获取码，在requestPermissions时传入
            case BRTMAP_PERMISSION_CODE:
                boolean isAllGrant = true;
                for (int grantResult: grantResults) {
                    if (grantResult != PackageManager.PERMISSION_GRANTED) {
                        isAllGrant = false;
                        break;
                    }
                }
                if (!isAllGrant) {
                    Toast.makeText(getApplicationContext(), "获取位置权限失败，请手动前往设置开启", Toast.LENGTH_SHORT).show();
                    return;
                }
                initMapView();
                break;
            default:
                break;
        }
    }

    public int getNavMode() {
        return navMode;
    }

    public float getForwardDistanceDriving() {
        return forwardDistanceDriving;
    }

    public void setForwardDistanceDriving(float forwardDistanceDriving) {
        this.forwardDistanceDriving = forwardDistanceDriving;
    }

    public float getForwardDistanceWalking() {
        return forwardDistanceWalking;
    }

    public void setForwardDistanceWalking(float forwardDistanceWalking) {
        this.forwardDistanceWalking = forwardDistanceWalking;
    }

    public float getDistanceToEndDriving() {
        return distanceToEndDriving;
    }

    public void setDistanceToEndDriving(float distanceToEndDriving) {
        this.distanceToEndDriving = distanceToEndDriving;
    }

    public float getDistanceToEndWalking() {
        return distanceToEndWalking;
    }

    public void setDistanceToEndWalking(float distanceToEndWalking) {
        this.distanceToEndWalking = distanceToEndWalking;
    }

    public float getForwardDistanceSpeakDriving() {
        return forwardDistanceSpeakDriving;
    }

    public void setForwardDistanceSpeakDriving(float forwardDistanceSpeakDriving) {
        this.forwardDistanceSpeakDriving = forwardDistanceSpeakDriving;
    }

    public float getForwardDistanceSpeakWalking() {
        return forwardDistanceSpeakWalking;
    }

    public void setForwardDistanceSpeakWalking(float forwardDistanceSpeakWalking) {
        this.forwardDistanceSpeakWalking = forwardDistanceSpeakWalking;
    }

    public float getDistanceToReplanRoute() {
        return distanceToReplanRoute;
    }

    public void setDistanceToReplanRoute(float distanceToReplanRoute) {
        this.distanceToReplanRoute = distanceToReplanRoute;
    }

    public float getZoomLevelDefault() {
        return zoomLevelDefault;
    }

    public void setZoomLevelDefault(float zoomLevelDefault) {
        this.zoomLevelDefault = zoomLevelDefault;
    }

    public float getZoomLevelNavigation() {
        return zoomLevelNavigation;
    }

    public void setZoomLevelNavigation(float zoomLevelNavigation) {
        this.zoomLevelNavigation = zoomLevelNavigation;
    }

    public boolean isShowActualLocation() {
        return showActualLocation;
    }

    public void setShowActualLocation(boolean showActualLocation) {
        this.showActualLocation = showActualLocation;
    }

    public void speak(String content) {
        if (speechEngine != null) {
            speechEngine.speak(content);
        }
    }

    public void speak(String content, boolean stopBefore) {
        if (speechEngine != null) {
            if (stopBefore) {
                speechEngine.stop();
            }
            speechEngine.speak(content);
        }
    }

    private BRTPoi queryParkingPoi() {

        BRTPoiEntity parkingPoiEntity = null;

        if (!TextUtils.isEmpty(parkingPoiName)) {
            List<BRTPoiEntity> poiEntities = searchAdapter.queryPoi(parkingPoiName);
            for (BRTPoiEntity poiEntity: poiEntities) {
                if ( poiEntity.getCategoryId() == 110601) {
                    parkingPoiEntity = poiEntity;
                    break;
                }
            }
        }

        if (parkingPoiEntity == null && lastLocation != null) {
            Coordinate locationCoord = new Coordinate(lastLocation.getX(), lastLocation.getY());
            List<BRTPoiEntity> poiEntities = searchAdapter.queryPoiByRadius(BRTConvert.toLatLng(locationCoord.x, locationCoord.y), 50.0, lastLocation.getFloor());
            BRTPoiEntity nearestPoiEntity = null;
            double nearestDistance = Double.MAX_VALUE;
            for (BRTPoiEntity poiEntity: poiEntities) {
                if ( poiEntity.getCategoryId() != 110601)
                    continue;
                Coordinate poiEntityCoord = new Coordinate(poiEntity.getLabelX(), poiEntity.getLabelY());
                double poiEntityDistance = poiEntityCoord.distance(locationCoord);
                if (nearestPoiEntity == null || poiEntityDistance < nearestDistance) {
                    nearestPoiEntity = poiEntity;
                    nearestDistance = poiEntityDistance;
                    continue;
                }
            }
            parkingPoiEntity = nearestPoiEntity;
        }

        if (parkingPoiEntity != null) {
            BRTPoi poi = new BRTPoi();
            poi.setBuildingID(parkingPoiEntity.getBuildingId());
            poi.setCategoryId(String.valueOf(parkingPoiEntity.getCategoryId()));
            poi.setFloorNumber(parkingPoiEntity.getFloorNumber());
            poi.setLayer(parkingPoiEntity.getLayer());
            poi.setName(parkingPoiEntity.getName());
            poi.setPoiID(parkingPoiEntity.getPoiId());
            poi.setPoint(new BRTPoint(poi.getFloorNumber(),BRTConvert.toLatLng(parkingPoiEntity.getLabelX(), parkingPoiEntity.getLabelY())));
            return poi;
        }

        return null;
    }

    public BRTPoi getParkingPoi() {
        return parkingPoi;
    }

    private static final List<String> permissionsNeedCheck;
    static {
        permissionsNeedCheck = new LinkedList<>();
        permissionsNeedCheck.add(android.Manifest.permission.ACCESS_COARSE_LOCATION);
        permissionsNeedCheck.add(android.Manifest.permission.ACCESS_FINE_LOCATION);
        permissionsNeedCheck.add(android.Manifest.permission.WRITE_EXTERNAL_STORAGE);

        System.loadLibrary("BRTLocationEngine");
        System.loadLibrary("BRTMapSDK");
    }
}
